﻿using Core;
using Core.Attributes;
using Core.Authorization;
using Core.Interfaces.Users;
using Core.Models;
using Core.Persistence;
using Core.SystemLogs;
using Core.Tenant.Abstractions;
using Finbuckle.MultiTenant;
using Finbuckle.MultiTenant.Abstractions;
using Infrastructure.Tenant;
using Mapster;
using MediatR;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;

namespace BP.Plugin.Monitor.Application.Features.SystemLogs.Commands.SyncLog;

/// <summary>
/// 同步日志到租户数据库
/// </summary>
public class TenantLogSyncCommand : IRequest<Result<bool>>
{
   
}

public class TenantLogSyncCommandHandler(
    ICurrentUserService currentUserService,
    IMultiTenantContextAccessor<BoilerplateTenantInfo> multiTenant,
    IServiceProvider serviceProvider) : IRequestHandler<TenantLogSyncCommand, Result<bool>>
{

    public async Task<Result<bool>> Handle(TenantLogSyncCommand request, CancellationToken cancellationToken)
    {
        if (string.IsNullOrWhiteSpace(multiTenant.MultiTenantContext.TenantInfo?.ConnectionString))
        {
            throw new BoilerplateException("没有日志需要同步");
        }

        var tenantId = currentUserService.GetTenant();
        if (string.IsNullOrWhiteSpace(tenantId))
            throw new BoilerplateException("非法租户");

        if (!currentUserService.IsSuperAdmin)
            throw new BoilerplateException("禁止非系统管理员操作");

        using var scope = serviceProvider.CreateScope();
        var tenantService = scope.ServiceProvider.GetRequiredService<ITenantService>();
        var allTenants = await tenantService.GetAllAsync();

        var mainTenant = allTenants.First(x => x.Id == TenantConstants.Root.Id);
        var mainContextSetter = scope.ServiceProvider.GetRequiredService<IMultiTenantContextSetter>();
        mainContextSetter.MultiTenantContext = new MultiTenantContext<BoilerplateTenantInfo>()
        {
            TenantInfo = mainTenant.Adapt<BoilerplateTenantInfo>()
        };

        var mainLogDb = scope.ServiceProvider.GetRequiredService<ILogDbContext>();

        // ① 仅从主库查询未同步的日志
        var unsyncedLogs = await mainLogDb.SystemLogs
            .IgnoreQueryFilters()
            .Where(l => !l.Synced && EF.Property<string>(l, "TenantId") == tenantId)
            .OrderBy(l => l.Id)
            .AsNoTracking()
            .ToListAsync(cancellationToken);

        if (unsyncedLogs.Count == 0)
            throw new BoilerplateException("没有日志需要同步");

        // ② 生成插入租户库的日志
        var newLogs = unsyncedLogs.Select(l => new SystemLog
        {
            MainLogId = l.Id,
            TimeStamp = l.TimeStamp,
            Level = l.Level,
            Message = l.Message,
            MessageTemplate = l.MessageTemplate,
            Exception = l.Exception,
            UserName = l.UserName,
            ClientIP = l.ClientIP,
            ClientAgent = l.ClientAgent,
            Properties = l.Properties,
            LogEvent = l.LogEvent,
            Module = l.Module,
            Operation = l.Operation,
            Route = l.Route,
            HttpMethod = l.HttpMethod,
            OS = l.OS,
            Browser = l.Browser,
            ElapsedMilliseconds = l.ElapsedMilliseconds,
            Address = l.Address,
            StatusCode = l.StatusCode,
            Headers = l.Headers,
            Synced = true
        }).ToList();

        //  写入租户库
        var tenant = allTenants.First(x => x.Id == tenantId);
        using var tenantScope = serviceProvider.CreateScope();
        var tenantSetter = tenantScope.ServiceProvider.GetRequiredService<IMultiTenantContextSetter>();
        tenantSetter.MultiTenantContext = new MultiTenantContext<BoilerplateTenantInfo>()
        {
            TenantInfo = tenant.Adapt<BoilerplateTenantInfo>()
        };

        var tenantLogDb = tenantScope.ServiceProvider.GetRequiredService<ILogDbContext>();
        await tenantLogDb.SystemLogs.AddRangeAsync(newLogs, cancellationToken);
        await tenantLogDb.SaveChangesAsync(cancellationToken);

        await mainLogDb.SystemLogs
            .IgnoreQueryFilters()
            .Where(l => EF.Property<string>(l, "TenantId") == tenantId && !l.Synced)
            .ExecuteUpdateAsync(setters => setters
            .SetProperty(l => l.Synced, true), cancellationToken);

        return await Result<bool>.SucceedAsync(true);
    }
}
